package timing.ukulele.third.msgChannel.handle.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.google.common.base.Throwables;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import timing.ukulele.third.constant.MessageConstant;
import timing.ukulele.third.constant.CommonConstant;
import timing.ukulele.third.dictionary.msg.MsgChannelType;
import timing.ukulele.third.dictionary.msg.SendMessageType;
import timing.ukulele.third.msgChannel.handle.BaseHandler;
import timing.ukulele.third.msgChannel.model.RecallTaskInfo;
import timing.ukulele.third.msgChannel.model.SendTaskInfo;
import timing.ukulele.third.msgChannel.model.account.DingDingRobotAccount;
import timing.ukulele.third.msgChannel.model.content.DingDingRobotContentModel;
import timing.ukulele.third.msgChannel.param.HandleMessageBaseResponse;
import timing.ukulele.third.msgChannel.param.dingding.DingDingRobotParam;
import timing.ukulele.third.msgChannel.param.dingding.DingDingRobotResult;
import timing.ukulele.third.msgChannel.utils.AccountUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * 钉钉消息自定义机器人 消息处理器
 * https://open.dingtalk.com/document/group/custom-robot-access
 * @author zh
 */
@Slf4j
@Service
public class DingDingRobotHandler extends BaseHandler {

    @Autowired
    private AccountUtils accountUtils;

    public DingDingRobotHandler() {
        channelCode = MsgChannelType.DING_DING_ROBOT.getCode();
    }

    @Override
    public HandleMessageBaseResponse send(SendTaskInfo sendTaskInfo) {
        // 构造返回值
        HandleMessageBaseResponse result = new HandleMessageBaseResponse();
        try {
            // 获取钉钉机器人账号信息
            DingDingRobotAccount account = accountUtils.getAccountById(
                    sendTaskInfo.getSendAccountId(), DingDingRobotAccount.class);
            DingDingRobotParam dingDingRobotParam = assembleParam(sendTaskInfo);
            // 请求发送消息
            String httpResult = HttpUtil.post(assembleParamUrl(account), JSON.toJSONString(dingDingRobotParam));
            log.info("DingDingHandler#send response:{},params:{}", httpResult, JSON.toJSONString(sendTaskInfo));
            // 处理请求返回值
            DingDingRobotResult dingDingRobotResult = JSON.parseObject(httpResult, DingDingRobotResult.class);
            if (dingDingRobotResult.getErrCode() == 0) {
                result.setHandleFlag(true);
            }else{
                result.setHandleFlag(false);
            }
            result.setHandleMessage(dingDingRobotResult.getErrMsg());
        } catch (Exception e) {
            log.error("DingDingHandler#send fail!e:{},params:{}",
                    Throwables.getStackTraceAsString(e), JSON.toJSONString(sendTaskInfo));
            result.setHandleMessage(Throwables.getStackTraceAsString(e));
        }
        return result;
    }

    private DingDingRobotParam assembleParam(SendTaskInfo sendTaskInfo) {
        // 接收者相关
        DingDingRobotParam.AtVO atVo = DingDingRobotParam.AtVO.builder().build();
        if (MessageConstant.SEND_ALL.equals(CollUtil.getFirst(sendTaskInfo.getReceiver()))) {
            atVo.setIsAtAll(true);
        } else {
            atVo.setAtUserIds(new ArrayList<>(sendTaskInfo.getReceiver()));
        }
        // 消息类型以及内容相关
        DingDingRobotContentModel contentModel = (DingDingRobotContentModel) sendTaskInfo.getContentModel();
        DingDingRobotParam param = DingDingRobotParam.builder().at(atVo)
                .msgtype(SendMessageType.getDingDingRobotTypeByCode(contentModel.getSendType()))
                .build();
        if (SendMessageType.TEXT.getCode().equals(contentModel.getSendType())) {
            param.setText(DingDingRobotParam.TextVO.builder().content(contentModel.getContent()).build());
        }
        if (SendMessageType.MARKDOWN.getCode().equals(contentModel.getSendType())) {
            param.setMarkdown(DingDingRobotParam.MarkdownVO.builder().title(contentModel.getTitle()).text(contentModel.getContent()).build());
        }
        if (SendMessageType.LINK.getCode().equals(contentModel.getSendType())) {
            param.setLink(DingDingRobotParam.LinkVO.builder().title(contentModel.getTitle()).text(contentModel.getContent()).messageUrl(contentModel.getUrl()).picUrl(contentModel.getPicUrl()).build());
        }
        if (SendMessageType.NEWS.getCode().equals(contentModel.getSendType())) {
            List<DingDingRobotParam.FeedCardVO.LinksVO> linksVoS = JSON.parseArray(contentModel.getFeedCards(), DingDingRobotParam.FeedCardVO.LinksVO.class);
            DingDingRobotParam.FeedCardVO feedCardVO = DingDingRobotParam.FeedCardVO.builder().links(linksVoS).build();
            param.setFeedCard(feedCardVO);
        }
        if (SendMessageType.ACTION_CARD.getCode().equals(contentModel.getSendType())) {
            List<DingDingRobotParam.ActionCardVO.BtnsVO> btnsVoS = JSON.parseArray(contentModel.getBtns(), DingDingRobotParam.ActionCardVO.BtnsVO.class);
            DingDingRobotParam.ActionCardVO actionCardVO = DingDingRobotParam.ActionCardVO.builder().title(contentModel.getTitle()).text(contentModel.getContent()).btnOrientation(contentModel.getBtnOrientation()).btns(btnsVoS).build();
            param.setActionCard(actionCardVO);
        }
        return param;
    }

    /**
     * 拼装 url
     *
     * @param account
     * @return
     */
    private String assembleParamUrl(DingDingRobotAccount account) {
        long currentTimeMillis = System.currentTimeMillis();
        String sign = assembleSign(currentTimeMillis, account.getSecret());
        return (account.getWebhook() + "&timestamp=" + currentTimeMillis + "&sign=" + sign);
    }

    /**
     * 使用HmacSHA256算法计算签名
     *
     * @param currentTimeMillis
     * @param secret
     * @return
     */
    private String assembleSign(long currentTimeMillis, String secret) {
        String sign = "";
        try {
            String stringToSign = currentTimeMillis + String.valueOf(StrPool.C_LF) + secret;
            Mac mac = Mac.getInstance(CommonConstant.HMAC_SHA256_ENCRYPTION_ALGO);
            mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), CommonConstant.HMAC_SHA256_ENCRYPTION_ALGO));
            byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
            sign = URLEncoder.encode(new String(Base64.encodeBase64(signData), StandardCharsets.UTF_8), CommonConstant.CHARSET_UTF_8);
        } catch (Exception e) {
            log.error("DingDingHandler#assembleSign fail!:{}", Throwables.getStackTraceAsString(e));
        }
        return sign;
    }

    /**
     * 钉钉自定义机器人 不支持撤回消息
     * https://open.dingtalk.com/document/group/custom-robot-access
     * @param recallTaskInfo
     */
    @Override
    public HandleMessageBaseResponse recall(RecallTaskInfo recallTaskInfo) {
        return HandleMessageBaseResponse.builder().handleFlag(true).build();
    }

}

